GtkAccessibleValue *value;
GList *list, *l;
GtkATContext *target_ctx;
- const char *unique_name;
int i;
- unique_name = g_dbus_connection_get_unique_name (self->connection);
-
for (i = 0; i < G_N_ELEMENTS (map); i++)
{
if (!gtk_at_context_has_accessible_relation (ctx, map[i].r))
for (l = list; l; l = l->next)
{
target_ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (l->data));
- g_variant_builder_add (&b, "(so)",
- unique_name,
- GTK_AT_SPI_CONTEXT (target_ctx)->context_path);
+
+ /* Realize the ATContext of the target, so we can ask for its ref */
+ gtk_at_context_realize (target_ctx);
+
+ g_variant_builder_add (&b, "@(so)",
+ gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (target_ctx)));
}
g_variant_builder_add (builder, "(ua(so))", map[i].s, &b);
GtkATContext *context = NULL;
GtkAccessible *accessible;
int idx, real_idx = 0;
- const char *name;
- const char *path;
g_variant_get (parameters, "(i)", &idx);
return;
}
- name = g_dbus_connection_get_unique_name (self->connection);
- path = gtk_at_spi_context_get_context_path (GTK_AT_SPI_CONTEXT (context));
+ /* Realize the child ATContext in order to get its ref */
+ gtk_at_context_realize (context);
+
+ GVariant *ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
- g_dbus_method_invocation_return_value (invocation, g_variant_new ("((so))", name, path));
+ g_dbus_method_invocation_return_value (invocation, g_variant_new ("(@(so))", ref));
}
else if (g_strcmp0 (method_name, "GetChildren") == 0)
{
GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
- const char *name = g_dbus_connection_get_unique_name (self->connection);
- const char *path = gtk_at_spi_context_get_context_path (GTK_AT_SPI_CONTEXT (context));
+ /* Realize the child ATContext in order to get its ref */
+ gtk_at_context_realize (context);
- g_variant_builder_add (&builder, "(so)", name, path);
+ GVariant *ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
+
+ if (ref != NULL)
+ g_variant_builder_add (&builder, "@(so)", ref);
}
}
else if (GTK_IS_STACK_PAGE (accessible))
if (gtk_accessible_should_present (GTK_ACCESSIBLE (child)))
{
GtkATContext *context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (child));
- const char *name = g_dbus_connection_get_unique_name (self->connection);
- const char *path = gtk_at_spi_context_get_context_path (GTK_AT_SPI_CONTEXT (context));
- g_variant_builder_add (&builder, "(so)", name, path);
+
+ /* Realize the child ATContext in order to get its ref */
+ gtk_at_context_realize (context);
+
+ GVariant *ref = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (context));
+
+ if (ref != NULL)
+ g_variant_builder_add (&builder, "@(so)", ref);
}
}
else if (g_strcmp0 (method_name, "GetRelationSet") == 0)
{
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a(ua(so))"));
- collect_relations (self, &builder);
+ collect_relations (self, &builder);
g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a(ua(so)))", &builder));
}
else if (g_strcmp0 (method_name, "GetInterfaces") == 0)
gtk_accessible_get_at_context (GTK_ACCESSIBLE (page));
if (parent_context != NULL)
- res = g_variant_new ("(so)",
- g_dbus_connection_get_unique_name (self->connection),
- GTK_AT_SPI_CONTEXT (parent_context)->context_path);
+ res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context));
}
else
{
gtk_accessible_get_at_context (GTK_ACCESSIBLE (parent));
if (parent_context != NULL)
- res = g_variant_new ("(so)",
- g_dbus_connection_get_unique_name (self->connection),
- GTK_AT_SPI_CONTEXT (parent_context)->context_path);
+ res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context));
}
}
else if (GTK_IS_STACK_PAGE (accessible))
gtk_accessible_get_at_context (GTK_ACCESSIBLE (parent));
if (parent_context != NULL)
- res = g_variant_new ("(so)",
- g_dbus_connection_get_unique_name (self->connection),
- GTK_AT_SPI_CONTEXT (parent_context)->context_path);
+ res = gtk_at_spi_context_to_ref (GTK_AT_SPI_CONTEXT (parent_context));
}
if (res == NULL)
int end,
const char *text)
{
+ if (self->connection == NULL)
+ return;
+
g_dbus_connection_emit_signal (self->connection,
NULL,
self->context_path,
const char *kind,
int cursor_position)
{
+ if (self->connection == NULL)
+ return;
+
if (strcmp (kind, "text-caret-moved") == 0)
g_dbus_connection_emit_signal (self->connection,
NULL,
emit_selection_changed (GtkAtSpiContext *self,
const char *kind)
{
+ if (self->connection == NULL)
+ return;
+
g_dbus_connection_emit_signal (self->connection,
NULL,
self->context_path,
const char *name,
gboolean enabled)
{
+ if (self->connection == NULL)
+ return;
+
g_dbus_connection_emit_signal (self->connection,
NULL,
self->context_path,
NULL);
}
+static void
+emit_defunct (GtkAtSpiContext *self)
+{
+ if (self->connection == NULL)
+ return;
+
+ g_dbus_connection_emit_signal (self->connection,
+ NULL,
+ self->context_path,
+ "org.a11y.atspi.Event.Object",
+ "StateChanged",
+ g_variant_new ("(siiva{sv})", "defunct", TRUE, 0, g_variant_new_string ("0"), NULL),
+ NULL);
+}
+
static void
emit_property_changed (GtkAtSpiContext *self,
const char *name,
GVariant *value)
{
+ if (self->connection == NULL)
+ return;
+
g_dbus_connection_emit_signal (self->connection,
NULL,
self->context_path,
int width,
int height)
{
+ if (self->connection == NULL)
+ return;
+
g_dbus_connection_emit_signal (self->connection,
NULL,
self->context_path,
int idx,
GtkAccessibleChildState state)
{
- GVariant *child_ref = gtk_at_spi_context_to_ref (child_context);
+ /* If we don't have a connection on either contexts, we cannot emit a signal */
+ if (self->connection == NULL || child_context->connection == NULL)
+ return;
+
GVariant *context_ref = gtk_at_spi_context_to_ref (self);
+ GVariant *child_ref = gtk_at_spi_context_to_ref (child_context);
gtk_at_spi_emit_children_changed (self->connection,
self->context_path,
}
self->interfaces = g_variant_ref_sink (g_variant_builder_end (&interfaces));
+
+ GTK_NOTE (A11Y, g_message ("Registered %d interfaces on object path '%s'",
+ self->n_registered_objects,
+ self->context_path));
}
static void
/* }}} */
/* {{{ GObject boilerplate */
static void
-gtk_at_spi_context_dispose (GObject *gobject)
+gtk_at_spi_context_finalize (GObject *gobject)
{
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
- GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
gtk_at_spi_context_unregister_object (self);
- gtk_atspi_disconnect_text_signals (accessible);
- gtk_atspi_disconnect_selection_signals (accessible);
-
- G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->dispose (gobject);
-}
-
-static void
-gtk_at_spi_context_finalize (GObject *gobject)
-{
- GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
g_free (self->bus_address);
g_free (self->context_path);
gtk_at_spi_context_constructed (GObject *gobject)
{
GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
- GdkDisplay *display;
+ GdkDisplay *display = gtk_at_context_get_display (GTK_AT_CONTEXT (self));
g_assert (self->bus_address);
- /* Every GTK application has a single root AT-SPI object, which
- * handles all the global state, including the cache of accessible
- * objects. We use the GdkDisplay to store it, so it's guaranteed
- * to be unique per-display connection
- */
- display = gtk_at_context_get_display (GTK_AT_CONTEXT (self));
- self->root =
- g_object_get_data (G_OBJECT (display), "-gtk-atspi-root");
-
- if (self->root == NULL)
- {
- self->root = gtk_at_spi_root_new (self->bus_address);
- g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-root",
- self->root,
- g_object_unref);
- }
-
- self->connection = gtk_at_spi_root_get_connection (self->root);
-
/* We use the application's object path to build the path of each
* accessible object exposed on the accessibility bus; the path is
* also used to access the object cache
g_free (base_path);
g_free (uuid);
- GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
+ /* Every GTK application has a single root AT-SPI object, which
+ * handles all the global state, including the cache of accessible
+ * objects. We use the GdkDisplay to store it, so it's guaranteed
+ * to be unique per-display connection
+ */
+ self->root =
+ g_object_get_data (G_OBJECT (display), "-gtk-atspi-root");
+
+ if (self->root == NULL)
+ {
+ self->root = gtk_at_spi_root_new (self->bus_address);
+ g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-root",
+ self->root,
+ g_object_unref);
+ }
+
+ G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject);
+}
+
+static void
+gtk_at_spi_context_realize (GtkATContext *context)
+{
+ GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
+
+ self->connection = gtk_at_spi_root_get_connection (self->root);
+ if (self->connection == NULL)
+ return;
+
+ GtkAccessible *accessible = gtk_at_context_get_accessible (context);
+ GTK_NOTE (A11Y, g_message ("Realizing ATSPI context at '%s' for accessible '%s'",
+ self->context_path,
+ G_OBJECT_TYPE_NAME (accessible)));
+
gtk_atspi_connect_text_signals (accessible,
(GtkAtspiTextChangedCallback *)emit_text_changed,
(GtkAtspiTextSelectionCallback *)emit_text_selection_changed,
self);
gtk_at_spi_context_register_object (self);
- G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject);
+ gtk_at_spi_root_queue_register (self->root);
+}
+
+static void
+gtk_at_spi_context_unrealize (GtkATContext *context)
+{
+ GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
+ GtkAccessible *accessible = gtk_at_context_get_accessible (context);
+
+ GTK_NOTE (A11Y, g_message ("Unrealizing ATSPI context at '%s' for accessible '%s'",
+ self->context_path,
+ G_OBJECT_TYPE_NAME (accessible)));
+
+ /* Notify ATs that the accessible object is going away */
+ emit_defunct (self);
+
+ gtk_atspi_disconnect_text_signals (accessible);
+ gtk_atspi_disconnect_selection_signals (accessible);
+ gtk_at_spi_context_unregister_object (self);
}
static void
gobject_class->set_property = gtk_at_spi_context_set_property;
gobject_class->get_property = gtk_at_spi_context_get_property;
gobject_class->finalize = gtk_at_spi_context_finalize;
- gobject_class->dispose = gtk_at_spi_context_dispose;
+ context_class->realize = gtk_at_spi_context_realize;
+ context_class->unrealize = gtk_at_spi_context_unrealize;
context_class->state_change = gtk_at_spi_context_state_change;
context_class->platform_change = gtk_at_spi_context_platform_change;
context_class->bounds_change = gtk_at_spi_context_bounds_change;
char *desktop_path;
gint32 application_id;
+ guint register_id;
GtkAtSpiCache *cache;
{
GtkAtSpiRoot *self = GTK_AT_SPI_ROOT (gobject);
+ g_clear_handle_id (&self->register_id, g_source_remove);
+
g_free (self->bus_address);
g_free (self->desktop_name);
g_free (self->desktop_path);
self->toplevels = gtk_window_get_toplevels ();
}
-static void
-gtk_at_spi_root_register (GtkAtSpiRoot *self)
+static gboolean
+root_register (gpointer data)
{
+ GtkAtSpiRoot *self = data;
+ const char *unique_name;
+
/* Register the root element; every application has a single root, so we only
* need to do this once.
*
self->atspi_version = ATSPI_VERSION;
self->root_path = ATSPI_ROOT_PATH;
+ unique_name = g_dbus_connection_get_unique_name (self->connection);
+
g_dbus_connection_register_object (self->connection,
self->root_path,
(GDBusInterfaceInfo *) &atspi_application_interface,
NULL);
GTK_NOTE (A11Y, g_message ("Registering (%s, %s) on the a11y bus",
- g_dbus_connection_get_unique_name (self->connection),
+ unique_name,
self->root_path));
g_dbus_connection_call (self->connection,
ATSPI_ROOT_PATH,
"org.a11y.atspi.Socket",
"Embed",
- g_variant_new ("((so))",
- g_dbus_connection_get_unique_name (self->connection),
- self->root_path
- ),
+ g_variant_new ("((so))", unique_name, self->root_path),
G_VARIANT_TYPE ("((so))"),
G_DBUS_CALL_FLAGS_NONE, -1,
NULL,
on_registration_reply,
self);
+
+ self->register_id = 0;
+
+ return G_SOURCE_REMOVE;
+}
+
+/*< private >
+ * gtk_at_spi_root_queue_register:
+ * @self: a #GtkAtSpiRoot
+ *
+ * Queues the registration of the root object on the AT-SPI bus.
+ */
+void
+gtk_at_spi_root_queue_register (GtkAtSpiRoot *self)
+{
+ /* Ignore multiple registration requests while one is already in flight */
+ if (self->register_id != 0)
+ return;
+
+ /* The cache is only available once the registration succeeds */
+ if (self->cache != NULL)
+ return;
+
+ self->register_id = g_idle_add (root_register, self);
+ g_source_set_name_by_id (self->register_id, "[gtk] ATSPI root registration");
}
static void
goto out;
}
- gtk_at_spi_root_register (self);
-
out:
G_OBJECT_CLASS (gtk_at_spi_root_parent_class)->constructed (gobject);
}